Enable RGBA windows on W32
authorРуслан Ижбулатов <lrn1986@gmail.com>
Wed, 22 Apr 2015 19:10:55 +0000 (19:10 +0000)
committerРуслан Ижбулатов <lrn1986@gmail.com>
Wed, 29 Apr 2015 21:12:13 +0000 (21:12 +0000)
Requires Vista and newer.

* Create surfaces with cairo_win32_surface_create_with_format
* Provide an rgba visual that can be distinguished from the system visual
* Make rgba visual the best available visual
* Enable alpha-transparency for all windows that we control
* Check for appropriate cairo capabilities at configure time
  (W32 - 1.14.3 newer than 2015-04-14; others - 1.14.0)

* Check for composition support before enabling CSDs
* Re-enable transparency on WM_DWMCOMPOSITIONCHANGED
Windows that were created while composition was enabled and that were CSDed
as a result and will look ugly (thick black borders or no borders at all) once
composition is disabled.
If composition is enabled afterwards, they will return back to normal.
This happens, for example, when RDP session is opened to a desktop where a GTK
application is running. For W7/Vista windows will only re-gain transparency after
the RDP session is closed. For W8 transparency will only be gone momentarily.

Windows that were created while composition was disabled will not be CSDed
automatically and will use SSD (WM decorations), while windows that are CSDed
manually will get a thin square border.
If composition is enabled afterwards, these windows will not change.
This is most noticeable for system menus (popup menus are often generated
on the fly, system menus are created once) and some dialogues (About dialogue,
for example).

https://bugzilla.gnome.org/show_bug.cgi?id=727316

configure.ac
gdk/win32/gdkevents-win32.c
gdk/win32/gdkprivate-win32.h
gdk/win32/gdkscreen-win32.c
gdk/win32/gdkvisual-win32.c
gdk/win32/gdkwindow-win32.c
gtk/gtkwindow.c

index a66606ea5b3f068107310e1cc686434a67ebb9cc..a05765300e422ae6718455953201c16f20850c9d 100644 (file)
@@ -576,6 +576,21 @@ PKG_CHECK_MODULES(BASE_DEPENDENCIES,
 
 PKG_CHECK_MODULES(CAIRO_BACKEND, [$cairo_backends])
 
+# Remove this check once cairo_required_version reaches at least 1.14.4
+case $host_os in
+  mingw*)
+    PKG_CHECK_MODULES(CAIRO, [cairo >= cairo_required_version])
+    AC_CHECK_LIB([cairo], [cairo_win32_surface_create_with_format],
+      [AC_MSG_RESULT([found])],
+      [AC_MSG_ERROR(
+[cairo_win32_surface_create_with_format is not found in cairo library
+You need cairo version newer than 2015-04-14])])
+    ;;
+  *)
+    ;;
+esac
+
+
 PKG_CHECK_MODULES(GMODULE, [gmodule-2.0])
 
 dnl ******************************************************
index 7ff7c082321f5c4138ceea80921b276b1a446ab5..f9b5b6a478c301a64aebcad70cd598c5d30c824d 100644 (file)
@@ -3171,6 +3171,10 @@ gdk_event_translate (MSG  *msg,
       handle_display_change ();
       break;
       
+    case WM_DWMCOMPOSITIONCHANGED:
+      _gdk_win32_window_enable_transparency (window);
+      break;
+      
     case WM_DESTROYCLIPBOARD:
       if (!_ignore_destroy_clipboard)
        {
index e018371ba83218516d6573d3cb261fb411b2be9e..932a39be43038d7c045b61e2feba11c2a7654349 100644 (file)
@@ -152,6 +152,9 @@ void _gdk_window_move_resize_child (GdkWindow *window,
                                    gint       width,
                                    gint       height);
 
+gboolean _gdk_win32_window_enable_transparency (GdkWindow *window);
+
+
 /* GdkWindowImpl methods */
 void _gdk_win32_window_scroll (GdkWindow *window,
                               gint       dx,
@@ -476,6 +479,7 @@ void _gdk_win32_window_delete_property (GdkWindow *window, GdkAtom    property);
 
 /* Stray GdkWin32Screen members */
 GdkVisual *_gdk_win32_screen_get_system_visual (GdkScreen *screen);
+GdkVisual *_gdk_win32_screen_get_rgba_visual (GdkScreen *screen);
 gboolean _gdk_win32_screen_get_setting (GdkScreen   *screen, const gchar *name, GValue *value);
 gint _gdk_win32_screen_visual_get_best_depth (GdkScreen *screen);
 GdkVisualType _gdk_win32_screen_visual_get_best_type (GdkScreen *screen);
index 4e9cc992b1ac8b0e7d12ad12eef8436ca80dc5dc..c637b95b81453f8452f5c0214ca51d4ee902346d 100644 (file)
@@ -156,14 +156,6 @@ gdk_win32_screen_get_monitor_geometry (GdkScreen    *screen,
   *dest = _gdk_monitors[num_monitor].rect;
 }
 
-static GdkVisual *
-gdk_win32_screen_get_rgba_visual (GdkScreen *screen)
-{
-  g_return_val_if_fail (screen == _gdk_screen, NULL);
-
-  return NULL;
-}
-
 static gint
 gdk_win32_screen_get_number (GdkScreen *screen)
 {
@@ -245,7 +237,7 @@ gdk_win32_screen_class_init (GdkWin32ScreenClass *klass)
   screen_class->get_monitor_geometry = gdk_win32_screen_get_monitor_geometry;
   screen_class->get_monitor_workarea = gdk_win32_screen_get_monitor_geometry;
   screen_class->get_system_visual = _gdk_win32_screen_get_system_visual;
-  screen_class->get_rgba_visual = gdk_win32_screen_get_rgba_visual;
+  screen_class->get_rgba_visual = _gdk_win32_screen_get_rgba_visual;
   screen_class->is_composited = gdk_win32_screen_is_composited;
   screen_class->make_display_name = gdk_win32_screen_make_display_name;
   screen_class->get_active_window = gdk_win32_screen_get_active_window;
index 34ee4340ed2e9cb4c9e94aeca0040d69e9e50c02..a9ffdf3f7a05bc5d56436a483babab2b6c9cd9ad 100644 (file)
@@ -36,14 +36,16 @@ static void  gdk_visual_decompose_mask (gulong     mask,
                                        gint      *prec);
 
 static GdkVisual *system_visual = NULL;
+static GdkVisual *rgba_visual = NULL;
 
 static gint available_depths[1];
 
 static GdkVisualType available_types[1];
 
-void
-_gdk_visual_init (GdkScreen *screen)
+static void
+_gdk_visual_init_internal (GdkScreen *screen, gboolean is_rgba)
 {
+  GdkVisual *visual;
   struct
   {
     BITMAPINFOHEADER bi;
@@ -60,14 +62,14 @@ _gdk_visual_init (GdkScreen *screen)
   gint bitspixel = GetDeviceCaps (_gdk_display_hdc, BITSPIXEL);
   gint map_entries = 0;
 
-  system_visual = g_object_new (GDK_TYPE_VISUAL, NULL);
-  system_visual->screen = screen;
+  visual = g_object_new (GDK_TYPE_VISUAL, NULL);
+  visual->screen = screen;
 
   if (rastercaps & RC_PALETTE)
     {
       const int sizepalette = GetDeviceCaps (_gdk_display_hdc, SIZEPALETTE);
       gchar *max_colors = getenv ("GDK_WIN32_MAX_COLORS");
-      system_visual->type = GDK_VISUAL_PSEUDO_COLOR;
+      visual->type = GDK_VISUAL_PSEUDO_COLOR;
 
       g_assert (sizepalette == 256);
 
@@ -81,7 +83,7 @@ _gdk_visual_init (GdkScreen *screen)
          if (map_entries < 32)
            {
              map_entries = 16;
-             system_visual->type = GDK_VISUAL_STATIC_COLOR;
+             visual->type = GDK_VISUAL_STATIC_COLOR;
              bitspixel = 4;
            }
          else if (map_entries < 64)
@@ -108,27 +110,27 @@ _gdk_visual_init (GdkScreen *screen)
   else if (bitspixel == 1 && numcolors == 16)
     {
       bitspixel = 4;
-      system_visual->type = GDK_VISUAL_STATIC_COLOR;
+      visual->type = GDK_VISUAL_STATIC_COLOR;
       map_entries = 16;
     }
   else if (bitspixel == 1)
     {
-      system_visual->type = GDK_VISUAL_STATIC_GRAY;
+      visual->type = GDK_VISUAL_STATIC_GRAY;
       map_entries = 2;
     }
   else if (bitspixel == 4)
     {
-      system_visual->type = GDK_VISUAL_STATIC_COLOR;
+      visual->type = GDK_VISUAL_STATIC_COLOR;
       map_entries = 16;
     }
   else if (bitspixel == 8)
     {
-      system_visual->type = GDK_VISUAL_STATIC_COLOR;
+      visual->type = GDK_VISUAL_STATIC_COLOR;
       map_entries = 256;
     }
   else if (bitspixel == 16)
     {
-      system_visual->type = GDK_VISUAL_TRUE_COLOR;
+      visual->type = GDK_VISUAL_TRUE_COLOR;
 #if 1
       /* This code by Mike Enright,
        * see http://www.users.cts.com/sd/m/menright/display.html
@@ -154,9 +156,9 @@ _gdk_visual_init (GdkScreen *screen)
            {
              /* It's 555 */
              bitspixel = 15;
-             system_visual->red_mask   = 0x00007C00;
-             system_visual->green_mask = 0x000003E0;
-             system_visual->blue_mask  = 0x0000001F;
+             visual->red_mask   = 0x00007C00;
+             visual->green_mask = 0x000003E0;
+             visual->blue_mask  = 0x0000001F;
            }
          else
            {
@@ -175,74 +177,87 @@ _gdk_visual_init (GdkScreen *screen)
              allmasks/=2;
            }
          bitspixel = k;
-         system_visual->red_mask = bmi.u.fields[0];
-         system_visual->green_mask = bmi.u.fields[1];
-         system_visual->blue_mask  = bmi.u.fields[2];
+         visual->red_mask = bmi.u.fields[0];
+         visual->green_mask = bmi.u.fields[1];
+         visual->blue_mask  = bmi.u.fields[2];
        }
 #else
       /* Old, incorrect (but still working) code. */
 #if 0
-      system_visual->red_mask   = 0x0000F800;
-      system_visual->green_mask = 0x000007E0;
-      system_visual->blue_mask  = 0x0000001F;
+      visual->red_mask   = 0x0000F800;
+      visual->green_mask = 0x000007E0;
+      visual->blue_mask  = 0x0000001F;
 #else
-      system_visual->red_mask   = 0x00007C00;
-      system_visual->green_mask = 0x000003E0;
-      system_visual->blue_mask  = 0x0000001F;
+      visual->red_mask   = 0x00007C00;
+      visual->green_mask = 0x000003E0;
+      visual->blue_mask  = 0x0000001F;
 #endif
 #endif
     }
   else if (bitspixel == 24 || bitspixel == 32)
     {
-      bitspixel = 24;
-      system_visual->type = GDK_VISUAL_TRUE_COLOR;
-      system_visual->red_mask   = 0x00FF0000;
-      system_visual->green_mask = 0x0000FF00;
-      system_visual->blue_mask  = 0x000000FF;
+      if (!is_rgba)
+        bitspixel = 24;
+      visual->type = GDK_VISUAL_TRUE_COLOR;
+      visual->red_mask   = 0x00FF0000;
+      visual->green_mask = 0x0000FF00;
+      visual->blue_mask  = 0x000000FF;
     }
   else
     g_error ("_gdk_visual_init: unsupported BITSPIXEL: %d\n", bitspixel);
 
-  system_visual->depth = bitspixel;
-  system_visual->byte_order = GDK_LSB_FIRST;
-  system_visual->bits_per_rgb = 42; /* Not used? */
+  visual->depth = bitspixel;
+  visual->byte_order = GDK_LSB_FIRST;
+  visual->bits_per_rgb = 42; /* Not used? */
 
-  if ((system_visual->type == GDK_VISUAL_TRUE_COLOR) ||
-      (system_visual->type == GDK_VISUAL_DIRECT_COLOR))
+  if ((visual->type == GDK_VISUAL_TRUE_COLOR) ||
+      (visual->type == GDK_VISUAL_DIRECT_COLOR))
     {
-      gdk_visual_decompose_mask (system_visual->red_mask,
-                                &system_visual->red_shift,
-                                &system_visual->red_prec);
-
-      gdk_visual_decompose_mask (system_visual->green_mask,
-                                &system_visual->green_shift,
-                                &system_visual->green_prec);
-
-      gdk_visual_decompose_mask (system_visual->blue_mask,
-                                &system_visual->blue_shift,
-                                &system_visual->blue_prec);
-      map_entries = 1 << (MAX (system_visual->red_prec,
-                              MAX (system_visual->green_prec,
-                                   system_visual->blue_prec)));
+      gdk_visual_decompose_mask (visual->red_mask,
+                                &visual->red_shift,
+                                &visual->red_prec);
+
+      gdk_visual_decompose_mask (visual->green_mask,
+                                &visual->green_shift,
+                                &visual->green_prec);
+
+      gdk_visual_decompose_mask (visual->blue_mask,
+                                &visual->blue_shift,
+                                &visual->blue_prec);
+      map_entries = 1 << (MAX (visual->red_prec,
+                              MAX (visual->green_prec,
+                                   visual->blue_prec)));
     }
   else
     {
-      system_visual->red_mask = 0;
-      system_visual->red_shift = 0;
-      system_visual->red_prec = 0;
+      visual->red_mask = 0;
+      visual->red_shift = 0;
+      visual->red_prec = 0;
 
-      system_visual->green_mask = 0;
-      system_visual->green_shift = 0;
-      system_visual->green_prec = 0;
+      visual->green_mask = 0;
+      visual->green_shift = 0;
+      visual->green_prec = 0;
 
-      system_visual->blue_mask = 0;
-      system_visual->blue_shift = 0;
-      system_visual->blue_prec = 0;
+      visual->blue_mask = 0;
+      visual->blue_shift = 0;
+      visual->blue_prec = 0;
     }
-  system_visual->colormap_size = map_entries;
+  visual->colormap_size = map_entries;
+
+  available_depths[0] = visual->depth;
+  available_types[0] = visual->type;
 
-  available_depths[0] = system_visual->depth;
-  available_types[0] = system_visual->type;
+  if (is_rgba)
+    rgba_visual = visual;
+  else
+    system_visual = visual;
+}
+
+void
+_gdk_visual_init (GdkScreen *screen)
+{
+  _gdk_visual_init_internal (screen, FALSE);
+  _gdk_visual_init_internal (screen, TRUE);
 }
 
 gint
@@ -263,16 +278,24 @@ _gdk_win32_screen_get_system_visual (GdkScreen *screen)
   return system_visual;
 }
 
+GdkVisual *
+_gdk_win32_screen_get_rgba_visual (GdkScreen *screen)
+{
+  return rgba_visual;
+}
+
 GdkVisual*
 _gdk_win32_screen_visual_get_best (GdkScreen *screen)
 {
-  return ((GdkVisual*) system_visual);
+  return ((GdkVisual*) rgba_visual);
 }
 
 GdkVisual*
 _gdk_win32_screen_visual_get_best_with_depth (GdkScreen *screen, gint depth)
 {
-  if (depth == system_visual->depth)
+  if (depth == rgba_visual->depth)
+    return (GdkVisual*) rgba_visual;
+  else if (depth == system_visual->depth)
     return (GdkVisual*) system_visual;
   else
     return NULL;
@@ -281,7 +304,9 @@ _gdk_win32_screen_visual_get_best_with_depth (GdkScreen *screen, gint depth)
 GdkVisual*
 _gdk_win32_screen_visual_get_best_with_type (GdkScreen *screen, GdkVisualType visual_type)
 {
-  if (visual_type == system_visual->type)
+  if (visual_type == rgba_visual->type)
+    return rgba_visual;
+  else if (visual_type == system_visual->type)
     return system_visual;
   else
     return NULL;
@@ -292,7 +317,9 @@ _gdk_win32_screen_visual_get_best_with_both (GdkScreen    *screen,
                                             gint          depth,
                                             GdkVisualType visual_type)
 {
-  if ((depth == system_visual->depth) && (visual_type == system_visual->type))
+  if ((depth == rgba_visual->depth) && (visual_type == rgba_visual->type))
+    return rgba_visual;
+  else if ((depth == system_visual->depth) && (visual_type == system_visual->type))
     return system_visual;
   else
     return NULL;
@@ -319,7 +346,12 @@ _gdk_win32_screen_query_visual_types (GdkScreen      *screen,
 GList*
 _gdk_win32_screen_list_visuals (GdkScreen *screen)
 {
-  return g_list_append (NULL, (gpointer) system_visual);
+  GList *result = NULL;
+
+  result = g_list_append (result, (gpointer) rgba_visual);
+  result = g_list_append (result, (gpointer) system_visual);
+
+  return result;
 }
 
 static void
index da85ded5e23592e2d61f5c27ab000e0f5b7c5aac..acb475b5e960bda9a08133facfc3cb5808c4180f 100644 (file)
@@ -41,6 +41,7 @@
 #include "gdkglcontext-win32.h"
 
 #include <cairo-win32.h>
+#include <dwmapi.h>
 
 static void gdk_window_impl_win32_init       (GdkWindowImplWin32      *window);
 static void gdk_window_impl_win32_class_init (GdkWindowImplWin32Class *klass);
@@ -245,6 +246,51 @@ _gdk_windowing_window_init (GdkScreen *screen)
   GDK_NOTE (MISC, g_print ("_gdk_root=%p\n", GDK_WINDOW_HWND (_gdk_root)));
 }
 
+gboolean
+_gdk_win32_window_enable_transparency (GdkWindow *window)
+{
+  DWM_BLURBEHIND blur_behind;
+  HRGN empty_region;
+  HRESULT call_result;
+  HWND parent, thiswindow;
+
+  if (window == NULL || GDK_WINDOW_HWND (window) == NULL)
+    return FALSE;
+
+  if (!gdk_screen_is_composited (gdk_window_get_screen (window)))
+    return FALSE;
+
+  if (window == _gdk_root)
+    return FALSE;
+
+  thiswindow = GDK_WINDOW_HWND (window);
+
+  /* Blurbehind only works on toplevel windows */
+  parent = GetAncestor (thiswindow, GA_PARENT);
+  if (!(GetWindowLong (thiswindow, GWL_STYLE) & WS_POPUP) &&
+      (parent == NULL || parent != GetDesktopWindow ()))
+    return FALSE;
+
+  empty_region = CreateRectRgn (0, 0, -1, -1);
+
+  if (empty_region == NULL)
+    return FALSE;
+
+  memset (&blur_behind, 0, sizeof (blur_behind));
+  blur_behind.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
+  blur_behind.hRgnBlur = empty_region;
+  blur_behind.fEnable = TRUE;
+  call_result = DwmEnableBlurBehindWindow (thiswindow, &blur_behind);
+
+  if (!SUCCEEDED (call_result))
+    g_warning ("%s: %s (%p) failed: %" G_GINT32_MODIFIER "x",
+        G_STRLOC, "DwmEnableBlurBehindWindow", thiswindow, (guint32) call_result);
+
+  DeleteObject (empty_region);
+
+  return SUCCEEDED (call_result);
+}
+
 static const gchar *
 get_default_title (void)
 {
@@ -490,7 +536,8 @@ _gdk_win32_display_create_window_impl (GdkDisplay    *display,
   window->impl = GDK_WINDOW_IMPL (impl);
 
   if (attributes_mask & GDK_WA_VISUAL)
-    g_assert (gdk_screen_get_system_visual (screen) == attributes->visual);
+    g_assert ((gdk_screen_get_system_visual (screen) == attributes->visual) ||
+              (gdk_screen_get_rgba_visual (screen) == attributes->visual));
 
   impl->override_redirect = override_redirect;
 
@@ -688,6 +735,8 @@ _gdk_win32_display_create_window_impl (GdkDisplay    *display,
 
   if (attributes_mask & GDK_WA_CURSOR)
     gdk_window_set_cursor (window, attributes->cursor);
+
+  _gdk_win32_window_enable_transparency (window);
 }
 
 GdkWindow *
@@ -3347,7 +3396,7 @@ gdk_win32_ref_cairo_surface (GdkWindow *window)
       if (!hdc)
        return NULL;
 
-      impl->cairo_surface = cairo_win32_surface_create (hdc);
+      impl->cairo_surface = cairo_win32_surface_create_with_format (hdc, CAIRO_FORMAT_ARGB32);
 
       cairo_surface_set_user_data (impl->cairo_surface, &gdk_win32_cairo_key,
                                   impl, gdk_win32_cairo_surface_destroy);
index 71d0ffe6c2c40f07a9d712ece990ec0ced594e9b..5a57b775f58801e38d8bb66090ce6fa834fe02cb 100644 (file)
@@ -4043,6 +4043,9 @@ gtk_window_supports_client_shadow (GtkWindow *window)
 
       screen = gtk_widget_get_screen (widget);
 
+      if (!gdk_screen_is_composited (screen))
+        return FALSE;
+
       /* We need a visual with alpha */
       visual = gdk_screen_get_rgba_visual (screen);
       if (!visual)